package com.gitee.sparta.core.cluster.balance;

import com.gitee.sparta.core.cluster.ClusterCache;
import lombok.extern.slf4j.Slf4j;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

/**
 * 链表轮询算法
 *
 * @author zyred
 * @since v 1.0.0
 */
@Slf4j
public class NodeLoadBalance implements LoadBalance {

    private final Set<String> ISOLATIONS = new HashSet<>();

    private volatile static int size = 0;
    private static NodeLoadBalance instance;

    private ServerNode head;
    private ServerNode tail;
    private ServerNode current;

    private NodeLoadBalance () {
    }

    @Override
    public String balance () {
        if (Objects.isNull(this.current)) {
            return null;
        }
        // 初始化从 第一个 开始
        return this.current.isolation;
    }

    @Override
    public synchronized void addNode (String isolation, Integer sid) {

        ServerNode snd = new ServerNode(isolation, sid);
        if (Objects.isNull(this.head)) {
            this.head = this.tail = snd;
            this.head.next = this.head;
            this.head.prev = this.head;

            ISOLATIONS.add(isolation);
            size ++;
            this.current = snd;
            return;
        }

        if (this.existNode(sid)) {
            return;
        }

        snd.prev = this.tail;
        snd.next = this.head;
        this.tail.next = snd;
        this.tail = snd;

        this.current = snd;
        ISOLATIONS.add(isolation);
        size ++;
        log.info("[Sparta] Add load balance, sid: {}, isolation: {}", sid, isolation);
    }

    @Override
    public synchronized void removeNode(Integer sid) {
        ServerNode node = this.head.next;
        while (node != this.head) {
            if (sid.equals(node.sid)) {
                node.prev.next = node.next;
                node.next.prev = node.prev;

                ISOLATIONS.remove(node.isolation);
                size --;
                return;
            }
            node = node.next;
        }
        log.info("[Sparta] Remove load balance, sid: {}", sid);
    }

    @Override
    public void switchover() {
        this.current = this.current.next;
    }

    @Override
    public void switchover(boolean jump, int size) {
        this.switchover();
        if (jump && this.size() > size) {
            return;
        }

        if (Objects.equals(this.current.isolation, ClusterCache.leaderIsolation)) {
            this.switchover();
        }
    }

    @Override
    public int size() {
        return size;
    }

    private boolean existNode (Integer sid) {
        ServerNode node = head.next;
        while (node != head) {
            if (sid.equals(node.sid)) {
                return true;
            }
            node = node.next;
        }
        return false;
    }

    @Override
    public boolean contains(String isolation) {
        return ISOLATIONS.contains(isolation);
    }

    public static NodeLoadBalance getNodeLoadBalanceInstance () {
        if (Objects.isNull(instance)) {
            synchronized (NodeLoadBalance.class) {
                if (Objects.isNull(instance)) {
                    instance = new NodeLoadBalance();
                }
            }
        }
        return instance;
    }
}
